library(GGally)
library(tidyr)
library(plotly)
library(plyr)
library(dplyr)
library(MASS)
library(vcd)
library(tidyquant)
\(1.\) Parallel Coordinates
\((a)\) Draw a parallel coordinates plot of the data in “ManhattanCDResults.csv” in the data folder on CourseWorks. (Original data source and additional information about the data can be found here: https://cbcny.org/research/nyc-resident-feedback-survey-community-district-results). Your plot should have one line for each of the twelve Manhattan community districts in the dataset.
cd <- read.csv('../data/ManhattanCDResults.csv')
cd[3:14] <- lapply(cd[3:14], function(x) as.numeric(sub("%", "", x)))
cd <- gather(cd, key="district", value="value", cd1:cd12)
cd <- cd[c(3,1,4)]
cd <- spread(cd, key=Indicator, value=value)
ggparcoord(cd,
columns = c(2:46),
groupColumn = "district",
scale = "globalminmax",
alphaLines = .7) +
coord_flip() +
xlab("Indicators") +
ylab("Approval")

\((b)\) Do there appear to be greater differences across indicators or across community districts? (In other words, are Manhattan community districts more alike or more different in how their citizens express their satisfaction with city life?
- There appears to be a greater differences across indicators than across community districts.
- Manahattan community districts are more a alike in how they express their satisfaction with the city life.
\((c)\) Which indicators have wide distributions (great variety) in responses?
- Neighborhood parks, Neightborhood as a place to live, Neighbrohood Playgrounds, Availability of cultural activities are the ones that stand out as having a wide distribution in responses.
\((d)\) Does there appear to be a correlation between districts and overall satisfaction? In order words, do some districts report high satisfaction on many indicators and some report low satisfaction on many indicators or are the results more mixed? (Hint: a different color for each community district helps identify these trends).
- There does appear to be a correlation between districts and overall satisfaction. There are some indicators that have higher variety in satisfaction than others, but ultimately there is less results that are mixed.
\(2.\) Mosaic Plots
Using the “Death2015.txt” data from the previous assignment, create a mosaic plot to identify whether Age is associated with Place of Death. Include only the top four Age categories. Treat Age as the independent variable and Place of Death as the dependent variable. (Hint: the dependent variable should be the last cut and it should be horizontal.) The labeling should be clear enough to identify what’s what, that is, “good enough,” not perfect. Do the variables appear to be associated? Describe briefly.
- (Assuming the “top four
Age categories”, means the top age categories meaning, 1, 1-4, 5-14, 15-24, that being the age from the top.)
- Place of death and age do appear to have some association. Very little.
- The likelihood of dying at the Descendent’s home increases with age.
- The likelihood of dying at the Medical Faility - Dead on Arrival appears to be higher for ages 1 or younger and decreases with age.
- The associations aren’t strong. They’re subtle, but some do exist.
death <- read.csv('../data/Death2015.txt', sep='\t')
ages = c("1", "1-4", "5-14", "15-24")
death <- subset(death, Ten.Year.Age.Groups.Code %in% ages)
death$Ten.Year.Age.Groups.Code <- factor(death$Ten.Year.Age.Groups.Code, levels=ages)
Really annoying (I couldn’t increase the size of the chart to see the labels.)
par(mar=c(7,8,7,7))
just_labels <- c("left", "center", "center", "center")
labels <- list(gp_labels = gpar(fontsize = 14, fontface = 3))
mosaic(Place.of.Death~Ten.Year.Age.Groups.Code, death, keep_aspect_ratio=FALSE, labeling = labeling_border(rot_labels = c(8,0,0,0), just_labels = just_labels))

\(3.\) Time Series
\((a)\) Use the tidyquant package to collect stock information on four stocks of your choosing. Create a line chart showing the closing price of the four stocks on the same graph, employing a different color for each stock.
from <- today() - years(1)
tickers <- c("AAPL", "MSFT", "AMZN", "GOOG")
stock.prices <- tq_get(tickers, get="stock.prices", from = from)
close.prices <- spread(stock_prices, key=symbol, value=close)
ggplot(stock.prices, aes(date, close, color=symbol)) +
geom_line()

\((b)\) Transform the data so each stock begins at 100 and replot. Do you learn anything new that wasn’t visible in part (a)?
- Apologies for the excessive code. I couldn’t find a better way of normalizing to a base-index of 100 so I wrote my own function. It would be awesome if you could show me a better way of doing this.
- We learn that the performance of GOOG and MSFT performed better than depicted in the line chart above.
- Indexing the data helps to better analyze the changes of price over time.
- Resource: https://www.dallasfed.org/research/basics/indexing.aspx
# Function to transform each stock/symbol to begin at 100.
normalize <- function(symbol){
index.value <- close.prices[[symbol]][1]/100
ans <- list()
for(price in close.prices[[symbol]]){
ans <- append(ans, (price/index.value))
}
df <- data.frame(symbol=unlist(ans))
colnames(df) <- c(symbol)
return(df)
}
close.prices$AMZN <- normalize('AMZN')$AMZN
close.prices$AAPL <- normalize('AAPL')$AAPL
close.prices$GOOG <- normalize('GOOG')$GOOG
close.prices$MSFT <- normalize('MSFT')$MSFT
ggplot(close.prices) +
geom_line(aes(date, AAPL, color="AAPL")) +
geom_line(aes(date, AMZN, color="AMZN")) +
geom_line(aes(date, MSFT, color="MSFT")) +
geom_line(aes(date, GOOG, color="GOOG")) +
xlab("") +
ylab("Close Price")

\(4.\) Missing Data
For this question, explore the New York State Feb 2017 snow accumulation dataset available in the data folder on CourseWorks: “NY-snowfall-201702.csv”. The original data source is here: https://www.ncdc.noaa.gov/snow-and-ice/daily-snow/
\((a)\) Show missing patterns graphically.
Set Up Snow data
snow.url <- "https://www.ncdc.noaa.gov/snow-and-ice/daily-snow/NY-snowfall-201802.csv"
main.snow <- data.frame(read.csv(snow.url, skip=1))
snow <- as.matrix(main.snow)
snow[snow == "T"] <- 0.01
snow[snow == "M"] <- NA
snow <- as.data.frame(snow)
rownames(snow) <- snow$Station.Name
Tidy Snow Data
tidysnow <- snow %>%
rownames_to_column("id") %>%
gather(key, value, -id) %>%
mutate(missing = ifelse(is.na(value), "yes", "no"))
Preserve key order
preserve.key.order <- function(){
ordered.keys <- list()
for(i in unique(tidysnow$key)){
ordered.keys <- append(ordered.keys, i)
}
return(ordered.keys)
}
tidysnow$key <- factor(tidysnow$key, levels=preserve.key.order())
Column Missing Pattern (geom_tile())
ggplot(tidysnow, aes(x = key, y = fct_rev(id), fill = missing)) +
geom_tile(color = "white") +
theme(axis.text.x = element_text(size=20,angle=90),
axis.text.y = element_text(size=20),
axis.title.x = element_text(size=40),
axis.title.y = element_text(size=40,angle=90)) +
xlab("") +
ylab("")

\((b)\) Is the percent of missing values consistent across days of the month, or is there variety?
- There are very few stations that are consistent with reporting values.
- There’s a lot of missing values and nothing is really consistent accross the days of the month; the values vary a lot.
- It would be interesting to sort by location and see if there’s a pattern in missing data.
\((c)\) Is the percent of missing values consistent across collection stations, or is there variety?
- It’s hard to tell if there’s a pattern in missing data. According to Missing Data by County, plot below, we could see that NYC under reports their values.
- However, there’s a lot of points clustered around NYC, and of course some of those are unreported.
- There’s a lot of variety in missing values across all the stations.
- The percent of missing values is inconsistent across collection stations and has lots of variety.
snow.url <- "https://www.ncdc.noaa.gov/snow-and-ice/daily-snow/NY-snowfall-201802.csv"
snow <- data.frame(read.csv(snow.url, skip=1))
snow$na.count <- apply(snow, 1, function(x) sum(x=='M'))
ggplot(snow, aes(x = Longitude, y = Latitude)) +
geom_point(aes(colour = na.count), alpha=.7) +
scale_colour_gradient(low = "grey", high = "green") +
ggtitle("Missing Data by County")

\((d)\) Is the daily average snowfall correlated with the daily missing values percent? On the basis of these results, what is your assessment of the reliability of the data to capture true snowfall patterns? In other words, based on what you’ve discovered, do you think that the missing data is highly problematic, or not?
- There does not seem to be a correlation between the average snowfall and missing values.
- Graphing both na_totals and daily average by each day we see a very weak negative correlation.
- Negative correlation makes sense, given that if average snow fall is high then the total_na should be low.
- The data is not that reliable in capturing true snowfall patterns.
- The missing data is highly problematic in capturing true snowfall patterns.
snow.url <- "https://www.ncdc.noaa.gov/snow-and-ice/daily-snow/NY-snowfall-201802.csv"
main.snow <- data.frame(read.csv(snow.url, skip=1))
snow <- as.matrix(main.snow)
snow[snow == "T"] <- 0.01
snow[snow == "M"] <- NA
snow <- as.data.frame(snow)
Create a Function to get na_total and average for each day of the month.
get.na_avg <- function(){
dates <- c(
"Feb.1","Feb.2","Feb.3","Feb.4","Feb.5","Feb.6","Feb.7","Feb.8","Feb.9","Feb.10",
"Feb.11","Feb.12","Feb.13","Feb.14","Feb.15","Feb.16","Feb.17","Feb.18","Feb.19",
"Feb.20","Feb.21","Feb.22","Feb.23","Feb.24","Feb.25","Feb.26","Feb.27","Feb.28"
)
na_total <- list()
average <- list()
date_list <- list()
for(date in dates){
na_total <- append(na_total, as.numeric(sum(is.na(snow[[date]]))))
average <- append(average, mean(as.numeric(snow[[date]]), na.rm = TRUE))
date_list <- append(date_list, date)
}
temp_df <- do.call(rbind.data.frame, Map('c', date_list, na_total, average))
colnames(temp_df) <- c('date', 'na_total', 'average')
# set up the data frame
temp_df$date <- factor(temp_df$date, levels=dates)
temp_df$na_total <- as.numeric(as.character(temp_df$na_total))
temp_df$average <- as.numeric(as.character(temp_df$average))
return(temp_df)
}
na_avg <- get.na_avg()
Get Correlation
cor(na_avg$na_total, na_avg$average)
[1] -0.4602537
Plot Scatter
ggplot(na_avg, aes(na_total, average)) +
geom_point() +
geom_smooth()

Plot Results over Time for fun
ggplot(na_avg) +
geom_line(aes(date, na_total, color="na_total", group = 1)) +
geom_line(aes(date, average, color="average", group = 1)) +
ggtitle("NA Total and Average Snow Fall For February")

Normalize Data Then Plot Again
# Function to transform each na_total and average to begin at 10.
normalize2 <- function(col){
index.value <- na_avg[[col]][1]/10
ans <- list()
for(i in na_avg[[col]]){
ans <- append(ans, (i/index.value))
}
temp_df <- data.frame(col=unlist(ans))
colnames(temp_df) <- c(col)
return(temp_df)
}
na_avg$na_total <- normalize2('na_total')$na_total
na_avg$average <- normalize2('average')$average
Normalized Plot
ggplot(na_avg) +
geom_line(aes(date, na_total, color="na_total", group = 1)) +
geom_line(aes(date, average, color="average", group = 1)) +
ggtitle("NA Total and Average Snow Fall For February (Normalized)")

LS0tCnRpdGxlOiAiSG9tZXdvcmsgMyIKYXV0aG9yOiBPbGVoIER1Ym5vCmRhdGU6IDAzLzA3LzE4Cm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSkKYGBgCgpgYGB7cn0KbGlicmFyeShHR2FsbHkpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KHBseXIpCmxpYnJhcnkoZHBseXIpIApsaWJyYXJ5KE1BU1MpCmxpYnJhcnkodmNkKQpsaWJyYXJ5KHRpZHlxdWFudCkKYGBgCioqJDEuJCBQYXJhbGxlbCBDb29yZGluYXRlcyoqCgokKGEpJCBEcmF3IGEgcGFyYWxsZWwgY29vcmRpbmF0ZXMgcGxvdCBvZiB0aGUgZGF0YSBpbiAiTWFuaGF0dGFuQ0RSZXN1bHRzLmNzdiIgaW4gdGhlIGRhdGEgZm9sZGVyIG9uIENvdXJzZVdvcmtzLiAoT3JpZ2luYWwgZGF0YSBzb3VyY2UgYW5kIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGRhdGEgY2FuIGJlIGZvdW5kIGhlcmU6ICBodHRwczovL2NiY255Lm9yZy9yZXNlYXJjaC9ueWMtcmVzaWRlbnQtZmVlZGJhY2stc3VydmV5LWNvbW11bml0eS1kaXN0cmljdC1yZXN1bHRzKS4gWW91ciBwbG90IHNob3VsZCBoYXZlIG9uZSBsaW5lIGZvciBlYWNoIG9mIHRoZSB0d2VsdmUgTWFuaGF0dGFuIGNvbW11bml0eSBkaXN0cmljdHMgaW4gdGhlIGRhdGFzZXQuIAoKYGBge3J9CmNkIDwtIHJlYWQuY3N2KCcuLi9kYXRhL01hbmhhdHRhbkNEUmVzdWx0cy5jc3YnKQpjZFszOjE0XSA8LSBsYXBwbHkoY2RbMzoxNF0sIGZ1bmN0aW9uKHgpIGFzLm51bWVyaWMoc3ViKCIlIiwgIiIsIHgpKSkKY2QgPC0gZ2F0aGVyKGNkLCBrZXk9ImRpc3RyaWN0IiwgdmFsdWU9InZhbHVlIiwgY2QxOmNkMTIpCmNkIDwtIGNkW2MoMywxLDQpXQpjZCA8LSBzcHJlYWQoY2QsIGtleT1JbmRpY2F0b3IsIHZhbHVlPXZhbHVlKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MTYsIGZpZy53aWR0aD0xMH0KZ2dwYXJjb29yZChjZCwgCiAgICAgICAgICAgY29sdW1ucyA9IGMoMjo0NiksIAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gImRpc3RyaWN0IiwgCiAgICAgICAgICAgc2NhbGUgPSAiZ2xvYmFsbWlubWF4IiwgCiAgICAgICAgICAgYWxwaGFMaW5lcyA9IC43KSArIAogIGNvb3JkX2ZsaXAoKSArIAogIHhsYWIoIkluZGljYXRvcnMiKSArIAogIHlsYWIoIkFwcHJvdmFsIikKYGBgCgoKJChiKSQgRG8gdGhlcmUgYXBwZWFyIHRvIGJlIGdyZWF0ZXIgZGlmZmVyZW5jZXMgYWNyb3NzICppbmRpY2F0b3JzKiBvciBhY3Jvc3MgKmNvbW11bml0eSBkaXN0cmljdHMqPyAoSW4gb3RoZXIgd29yZHMsIGFyZSBNYW5oYXR0YW4gY29tbXVuaXR5IGRpc3RyaWN0cyBtb3JlIGFsaWtlIG9yIG1vcmUgZGlmZmVyZW50IGluIGhvdyB0aGVpciBjaXRpemVucyBleHByZXNzIHRoZWlyIHNhdGlzZmFjdGlvbiB3aXRoIGNpdHkgbGlmZT8gCgoxLiBUaGVyZSBhcHBlYXJzIHRvIGJlIGEgZ3JlYXRlciBkaWZmZXJlbmNlcyBhY3Jvc3MgKmluZGljYXRvcnMqIHRoYW4gYWNyb3NzICpjb21tdW5pdHkgZGlzdHJpY3RzKi4gCjIuIE1hbmFoYXR0YW4gY29tbXVuaXR5IGRpc3RyaWN0cyBhcmUgbW9yZSBhIGFsaWtlIGluIGhvdyB0aGV5IGV4cHJlc3MgdGhlaXIgc2F0aXNmYWN0aW9uIHdpdGggdGhlIGNpdHkgbGlmZS4gCgokKGMpJCBXaGljaCBpbmRpY2F0b3JzIGhhdmUgd2lkZSBkaXN0cmlidXRpb25zIChncmVhdCB2YXJpZXR5KSBpbiByZXNwb25zZXM/CgoxLiAqTmVpZ2hib3Job29kIHBhcmtzKiwgKk5laWdodGJvcmhvb2QgYXMgYSBwbGFjZSB0byBsaXZlKiwgKk5laWdoYnJvaG9vZCBQbGF5Z3JvdW5kcyosICpBdmFpbGFiaWxpdHkgb2YgY3VsdHVyYWwgYWN0aXZpdGllcyogYXJlIHRoZSBvbmVzIHRoYXQgc3RhbmQgb3V0IGFzIGhhdmluZyBhIHdpZGUgZGlzdHJpYnV0aW9uIGluIHJlc3BvbnNlcy4KCiQoZCkkIERvZXMgdGhlcmUgYXBwZWFyIHRvIGJlIGEgY29ycmVsYXRpb24gYmV0d2VlbiBkaXN0cmljdHMgYW5kIG92ZXJhbGwgc2F0aXNmYWN0aW9uPyAgSW4gb3JkZXIgd29yZHMsIGRvIHNvbWUgZGlzdHJpY3RzIHJlcG9ydCBoaWdoIHNhdGlzZmFjdGlvbiBvbiBtYW55IGluZGljYXRvcnMgYW5kIHNvbWUgcmVwb3J0IGxvdyBzYXRpc2ZhY3Rpb24gb24gbWFueSBpbmRpY2F0b3JzIG9yIGFyZSB0aGUgcmVzdWx0cyBtb3JlIG1peGVkPyAoSGludDogYSBkaWZmZXJlbnQgY29sb3IgZm9yIGVhY2ggY29tbXVuaXR5IGRpc3RyaWN0IGhlbHBzIGlkZW50aWZ5IHRoZXNlIHRyZW5kcykuIAoKMS4gVGhlcmUgZG9lcyBhcHBlYXIgdG8gYmUgYSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGRpc3RyaWN0cyBhbmQgb3ZlcmFsbCBzYXRpc2ZhY3Rpb24uIFRoZXJlIGFyZSBzb21lIGluZGljYXRvcnMgdGhhdCBoYXZlIGhpZ2hlciB2YXJpZXR5IGluIHNhdGlzZmFjdGlvbiB0aGFuIG90aGVycywgYnV0IHVsdGltYXRlbHkgdGhlcmUgaXMgbGVzcyByZXN1bHRzIHRoYXQgYXJlIG1peGVkLiAKCioqJDIuJCBNb3NhaWMgUGxvdHMqKgoKVXNpbmcgdGhlICJEZWF0aDIwMTUudHh0IiBkYXRhIGZyb20gdGhlIHByZXZpb3VzIGFzc2lnbm1lbnQsIGNyZWF0ZSBhIG1vc2FpYyBwbG90IHRvIGlkZW50aWZ5IHdoZXRoZXIgYEFnZWAgaXMgYXNzb2NpYXRlZCB3aXRoIGBQbGFjZSBvZiBEZWF0aGAuIEluY2x1ZGUgb25seSB0aGUgdG9wIGZvdXIgYEFnZWAgY2F0ZWdvcmllcy4gVHJlYXQgYEFnZWAgYXMgdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlIGFuZCBgUGxhY2Ugb2YgRGVhdGhgIGFzIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUuIChIaW50OiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIHNob3VsZCBiZSB0aGUgbGFzdCBjdXQgYW5kIGl0IHNob3VsZCBiZSBob3Jpem9udGFsLikgVGhlIGxhYmVsaW5nIHNob3VsZCBiZSBjbGVhciBlbm91Z2ggdG8gaWRlbnRpZnkgd2hhdCdzIHdoYXQsIHRoYXQgaXMsICJnb29kIGVub3VnaCwiIG5vdCBwZXJmZWN0LiBEbyB0aGUgdmFyaWFibGVzIGFwcGVhciB0byBiZSBhc3NvY2lhdGVkPyBEZXNjcmliZSBicmllZmx5LgoKKiAoQXNzdW1pbmcgdGhlICJ0b3AgZm91ciBgQWdlYCBjYXRlZ29yaWVzIiwgbWVhbnMgdGhlIHRvcCBhZ2UgY2F0ZWdvcmllcyBtZWFuaW5nLCAxLCAxLTQsIDUtMTQsIDE1LTI0LCB0aGF0IGJlaW5nIHRoZSBhZ2UgZnJvbSB0aGUgdG9wLikKMS4gUGxhY2Ugb2YgZGVhdGggYW5kIGFnZSBkbyBhcHBlYXIgdG8gaGF2ZSBzb21lIGFzc29jaWF0aW9uLiBWZXJ5IGxpdHRsZS4KMi4gVGhlIGxpa2VsaWhvb2Qgb2YgZHlpbmcgYXQgdGhlICpEZXNjZW5kZW50J3MgaG9tZSogaW5jcmVhc2VzIHdpdGggYWdlLiAKMy4gVGhlIGxpa2VsaWhvb2Qgb2YgZHlpbmcgYXQgdGhlICpNZWRpY2FsIEZhaWxpdHkgLSBEZWFkIG9uIEFycml2YWwqIGFwcGVhcnMgdG8gYmUgaGlnaGVyIGZvciBhZ2VzIDEgb3IgeW91bmdlciBhbmQgZGVjcmVhc2VzIHdpdGggYWdlLgo0LiBUaGUgYXNzb2NpYXRpb25zIGFyZW4ndCBzdHJvbmcuIFRoZXkncmUgc3VidGxlLCBidXQgc29tZSBkbyBleGlzdC4KCmBgYHtyfQpkZWF0aCA8LSByZWFkLmNzdignLi4vZGF0YS9EZWF0aDIwMTUudHh0Jywgc2VwPSdcdCcpCmFnZXMgPSBjKCIxIiwgIjEtNCIsICI1LTE0IiwgIjE1LTI0IikKZGVhdGggPC0gc3Vic2V0KGRlYXRoLCBUZW4uWWVhci5BZ2UuR3JvdXBzLkNvZGUgJWluJSBhZ2VzKQpkZWF0aCRUZW4uWWVhci5BZ2UuR3JvdXBzLkNvZGUgPC0gZmFjdG9yKGRlYXRoJFRlbi5ZZWFyLkFnZS5Hcm91cHMuQ29kZSwgbGV2ZWxzPWFnZXMpCmBgYAojIyMjIyMgUmVhbGx5IGFubm95aW5nIChJIGNvdWxkbid0IGluY3JlYXNlIHRoZSBzaXplIG9mIHRoZSBjaGFydCB0byBzZWUgdGhlIGxhYmVscy4pCmBgYHtyIGZpZy53aWR0aD0xMn0KcGFyKG1hcj1jKDcsOCw3LDcpKQpqdXN0X2xhYmVscyA8LSBjKCJsZWZ0IiwgImNlbnRlciIsICJjZW50ZXIiLCAiY2VudGVyIikKbGFiZWxzIDwtIGxpc3QoZ3BfbGFiZWxzID0gZ3Bhcihmb250c2l6ZSA9IDE0LCBmb250ZmFjZSA9IDMpKQptb3NhaWMoUGxhY2Uub2YuRGVhdGh+VGVuLlllYXIuQWdlLkdyb3Vwcy5Db2RlLCBkZWF0aCwga2VlcF9hc3BlY3RfcmF0aW89RkFMU0UsIGxhYmVsaW5nID0gbGFiZWxpbmdfYm9yZGVyKHJvdF9sYWJlbHMgPSBjKDgsMCwwLDApLCBqdXN0X2xhYmVscyA9IGp1c3RfbGFiZWxzKSkgCmBgYAoKKiokMy4kIFRpbWUgU2VyaWVzKioKCiQoYSkkIFVzZSB0aGUgYHRpZHlxdWFudGAgcGFja2FnZSB0byBjb2xsZWN0IHN0b2NrIGluZm9ybWF0aW9uIG9uIGZvdXIgc3RvY2tzIG9mIHlvdXIgY2hvb3NpbmcuICBDcmVhdGUgYSBsaW5lIGNoYXJ0IHNob3dpbmcgdGhlIGNsb3NpbmcgcHJpY2Ugb2YgdGhlIGZvdXIgc3RvY2tzIG9uIHRoZSBzYW1lIGdyYXBoLCBlbXBsb3lpbmcgYSBkaWZmZXJlbnQgY29sb3IgZm9yIGVhY2ggc3RvY2suCmBgYHtyfQpmcm9tIDwtIHRvZGF5KCkgLSB5ZWFycygxKQp0aWNrZXJzIDwtIGMoIkFBUEwiLCAiTVNGVCIsICJBTVpOIiwgIkdPT0ciKQpzdG9jay5wcmljZXMgPC0gIHRxX2dldCh0aWNrZXJzLCBnZXQ9InN0b2NrLnByaWNlcyIsIGZyb20gPSBmcm9tKQpjbG9zZS5wcmljZXMgPC0gc3ByZWFkKHN0b2NrX3ByaWNlcywga2V5PXN5bWJvbCwgdmFsdWU9Y2xvc2UpCmBgYApgYGB7cn0KZ2dwbG90KHN0b2NrLnByaWNlcywgYWVzKGRhdGUsIGNsb3NlLCBjb2xvcj1zeW1ib2wpKSArCiAgICBnZW9tX2xpbmUoKQpgYGAKCgokKGIpJCBUcmFuc2Zvcm0gdGhlIGRhdGEgc28gZWFjaCBzdG9jayBiZWdpbnMgYXQgMTAwIGFuZCByZXBsb3QuIERvIHlvdSBsZWFybiBhbnl0aGluZyBuZXcgdGhhdCB3YXNuJ3QgdmlzaWJsZSBpbiBwYXJ0IChhKT8KCjEuIEFwb2xvZ2llcyBmb3IgdGhlIGV4Y2Vzc2l2ZSBjb2RlLiBJIGNvdWxkbid0IGZpbmQgYSBiZXR0ZXIgd2F5IG9mIG5vcm1hbGl6aW5nIHRvIGEgYmFzZS1pbmRleCBvZiAxMDAgc28gSSB3cm90ZSBteSBvd24gZnVuY3Rpb24uIEl0IHdvdWxkIGJlIGF3ZXNvbWUgaWYgeW91IGNvdWxkIHNob3cgbWUgYSBiZXR0ZXIgd2F5IG9mIGRvaW5nIHRoaXMuCjIuIFdlIGxlYXJuIHRoYXQgdGhlIHBlcmZvcm1hbmNlIG9mICpHT09HKiBhbmQgKk1TRlQqIHBlcmZvcm1lZCBiZXR0ZXIgdGhhbiBkZXBpY3RlZCBpbiB0aGUgbGluZSBjaGFydCBhYm92ZS4gCjMuIEluZGV4aW5nIHRoZSBkYXRhIGhlbHBzIHRvIGJldHRlciBhbmFseXplIHRoZSBjaGFuZ2VzIG9mIHByaWNlIG92ZXIgdGltZS4KNC4gUmVzb3VyY2U6IGh0dHBzOi8vd3d3LmRhbGxhc2ZlZC5vcmcvcmVzZWFyY2gvYmFzaWNzL2luZGV4aW5nLmFzcHgKCmBgYHtyfQojIEZ1bmN0aW9uIHRvIHRyYW5zZm9ybSBlYWNoIHN0b2NrL3N5bWJvbCB0byBiZWdpbiBhdCAxMDAuCm5vcm1hbGl6ZSA8LSBmdW5jdGlvbihzeW1ib2wpewogIGluZGV4LnZhbHVlIDwtIGNsb3NlLnByaWNlc1tbc3ltYm9sXV1bMV0vMTAwCiAgYW5zIDwtIGxpc3QoKQogIGZvcihwcmljZSBpbiBjbG9zZS5wcmljZXNbW3N5bWJvbF1dKXsKICAgIGFucyA8LSBhcHBlbmQoYW5zLCAocHJpY2UvaW5kZXgudmFsdWUpKQogIH0KICBkZiA8LSBkYXRhLmZyYW1lKHN5bWJvbD11bmxpc3QoYW5zKSkKICBjb2xuYW1lcyhkZikgPC0gYyhzeW1ib2wpCiAgcmV0dXJuKGRmKQp9CmBgYAoKYGBge3J9CmNsb3NlLnByaWNlcyRBTVpOIDwtIG5vcm1hbGl6ZSgnQU1aTicpJEFNWk4KY2xvc2UucHJpY2VzJEFBUEwgPC0gbm9ybWFsaXplKCdBQVBMJykkQUFQTApjbG9zZS5wcmljZXMkR09PRyA8LSBub3JtYWxpemUoJ0dPT0cnKSRHT09HCmNsb3NlLnByaWNlcyRNU0ZUIDwtIG5vcm1hbGl6ZSgnTVNGVCcpJE1TRlQKYGBgCgpgYGB7cn0KZ2dwbG90KGNsb3NlLnByaWNlcykgKyAKICBnZW9tX2xpbmUoYWVzKGRhdGUsIEFBUEwsIGNvbG9yPSJBQVBMIikpICsgCiAgZ2VvbV9saW5lKGFlcyhkYXRlLCBBTVpOLCBjb2xvcj0iQU1aTiIpKSArCiAgZ2VvbV9saW5lKGFlcyhkYXRlLCBNU0ZULCBjb2xvcj0iTVNGVCIpKSArCiAgZ2VvbV9saW5lKGFlcyhkYXRlLCBHT09HLCBjb2xvcj0iR09PRyIpKSArCiAgeGxhYigiIikgKwogIHlsYWIoIkNsb3NlIFByaWNlIikKYGBgCgoKKiokNC4kIE1pc3NpbmcgRGF0YSoqCgpGb3IgdGhpcyBxdWVzdGlvbiwgZXhwbG9yZSB0aGUgTmV3IFlvcmsgU3RhdGUgRmViIDIwMTcgc25vdyBhY2N1bXVsYXRpb24gZGF0YXNldCBhdmFpbGFibGUgaW4gdGhlIGRhdGEgZm9sZGVyIG9uIENvdXJzZVdvcmtzOiAiTlktc25vd2ZhbGwtMjAxNzAyLmNzdiIuIFRoZSBvcmlnaW5hbCBkYXRhIHNvdXJjZSBpcyBoZXJlOiBodHRwczovL3d3dy5uY2RjLm5vYWEuZ292L3Nub3ctYW5kLWljZS9kYWlseS1zbm93LwoKJChhKSQgU2hvdyBtaXNzaW5nIHBhdHRlcm5zIGdyYXBoaWNhbGx5LgoKIyMjIyMgU2V0IFVwIFNub3cgZGF0YQpgYGB7cn0Kc25vdy51cmwgPC0gImh0dHBzOi8vd3d3Lm5jZGMubm9hYS5nb3Yvc25vdy1hbmQtaWNlL2RhaWx5LXNub3cvTlktc25vd2ZhbGwtMjAxODAyLmNzdiIKbWFpbi5zbm93IDwtIGRhdGEuZnJhbWUocmVhZC5jc3Yoc25vdy51cmwsIHNraXA9MSkpCnNub3cgPC0gYXMubWF0cml4KG1haW4uc25vdykKc25vd1tzbm93ID09ICJUIl0gPC0gMC4wMQpzbm93W3Nub3cgPT0gIk0iXSA8LSBOQQpzbm93IDwtIGFzLmRhdGEuZnJhbWUoc25vdykKcm93bmFtZXMoc25vdykgPC0gc25vdyRTdGF0aW9uLk5hbWUKYGBgCiMjIyMjIFRpZHkgU25vdyBEYXRhCmBgYHtyfQp0aWR5c25vdyA8LSBzbm93ICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigiaWQiKSAlPiUKICBnYXRoZXIoa2V5LCB2YWx1ZSwgLWlkKSAlPiUKICBtdXRhdGUobWlzc2luZyA9IGlmZWxzZShpcy5uYSh2YWx1ZSksICJ5ZXMiLCAibm8iKSkKYGBgCiMjIyMjIFByZXNlcnZlICprZXkqIG9yZGVyCmBgYHtyfQpwcmVzZXJ2ZS5rZXkub3JkZXIgPC0gZnVuY3Rpb24oKXsKICBvcmRlcmVkLmtleXMgPC0gbGlzdCgpCiAgZm9yKGkgaW4gdW5pcXVlKHRpZHlzbm93JGtleSkpewogICAgb3JkZXJlZC5rZXlzIDwtIGFwcGVuZChvcmRlcmVkLmtleXMsIGkpCiAgfQogIHJldHVybihvcmRlcmVkLmtleXMpCn0KdGlkeXNub3cka2V5IDwtIGZhY3Rvcih0aWR5c25vdyRrZXksIGxldmVscz1wcmVzZXJ2ZS5rZXkub3JkZXIoKSkKYGBgCiMjIyMjIENvbHVtbiBNaXNzaW5nIFBhdHRlcm4gKGdlb21fdGlsZSgpKQpgYGB7ciwgZmlnLmhlaWdodD0xNTAsIGZpZy53aWR0aD0zMH0KZ2dwbG90KHRpZHlzbm93LCBhZXMoeCA9IGtleSwgeSA9IGZjdF9yZXYoaWQpLCBmaWxsID0gbWlzc2luZykpICsKICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0yMCxhbmdsZT05MCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksICAKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT00MCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9NDAsYW5nbGU9OTApKSArCiAgeGxhYigiIikgKyAKICB5bGFiKCIiKQpgYGAKCiQoYikkIElzIHRoZSBwZXJjZW50IG9mIG1pc3NpbmcgdmFsdWVzIGNvbnNpc3RlbnQgYWNyb3NzIGRheXMgb2YgdGhlIG1vbnRoLCBvciBpcyB0aGVyZSB2YXJpZXR5PwoKMS4gVGhlcmUgYXJlIHZlcnkgZmV3IHN0YXRpb25zIHRoYXQgYXJlIGNvbnNpc3RlbnQgd2l0aCByZXBvcnRpbmcgdmFsdWVzLgoyLiBUaGVyZSdzIGEgbG90IG9mIG1pc3NpbmcgdmFsdWVzIGFuZCBub3RoaW5nIGlzIHJlYWxseSBjb25zaXN0ZW50IGFjY3Jvc3MgdGhlIGRheXMgb2YgdGhlIG1vbnRoOyB0aGUgdmFsdWVzIHZhcnkgYSBsb3QuCjMuIEl0IHdvdWxkIGJlIGludGVyZXN0aW5nIHRvIHNvcnQgYnkgbG9jYXRpb24gYW5kIHNlZSBpZiB0aGVyZSdzIGEgcGF0dGVybiBpbiBtaXNzaW5nIGRhdGEuCgokKGMpJCBJcyB0aGUgcGVyY2VudCBvZiBtaXNzaW5nIHZhbHVlcyBjb25zaXN0ZW50IGFjcm9zcyBjb2xsZWN0aW9uIHN0YXRpb25zLCBvciBpcyB0aGVyZSB2YXJpZXR5PwoKMS4gSXQncyBoYXJkIHRvIHRlbGwgaWYgdGhlcmUncyBhIHBhdHRlcm4gaW4gbWlzc2luZyBkYXRhLiBBY2NvcmRpbmcgdG8gKk1pc3NpbmcgRGF0YSBieSBDb3VudHkqLCBwbG90IGJlbG93LCB3ZSBjb3VsZCBzZWUgdGhhdCBOWUMgdW5kZXIgcmVwb3J0cyB0aGVpciB2YWx1ZXMuCjIuIEhvd2V2ZXIsIHRoZXJlJ3MgYSBsb3Qgb2YgcG9pbnRzIGNsdXN0ZXJlZCBhcm91bmQgTllDLCBhbmQgb2YgY291cnNlIHNvbWUgb2YgdGhvc2UgYXJlIHVucmVwb3J0ZWQuIAozLiBUaGVyZSdzIGEgbG90IG9mIHZhcmlldHkgaW4gbWlzc2luZyB2YWx1ZXMgYWNyb3NzIGFsbCB0aGUgc3RhdGlvbnMuCjQuIFRoZSBwZXJjZW50IG9mIG1pc3NpbmcgdmFsdWVzIGlzIGluY29uc2lzdGVudCBhY3Jvc3MgY29sbGVjdGlvbiBzdGF0aW9ucyBhbmQgaGFzIGxvdHMgb2YgdmFyaWV0eS4KCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQpzbm93LnVybCA8LSAiaHR0cHM6Ly93d3cubmNkYy5ub2FhLmdvdi9zbm93LWFuZC1pY2UvZGFpbHktc25vdy9OWS1zbm93ZmFsbC0yMDE4MDIuY3N2Igpzbm93IDwtIGRhdGEuZnJhbWUocmVhZC5jc3Yoc25vdy51cmwsIHNraXA9MSkpCnNub3ckbmEuY291bnQgPC0gYXBwbHkoc25vdywgMSwgZnVuY3Rpb24oeCkgc3VtKHg9PSdNJykpCmdncGxvdChzbm93LCBhZXMoeCA9IExvbmdpdHVkZSwgeSA9IExhdGl0dWRlKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IG5hLmNvdW50KSwgYWxwaGE9LjcpICsgCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50KGxvdyA9ICJncmV5IiwgaGlnaCA9ICJncmVlbiIpICsgCiAgZ2d0aXRsZSgiTWlzc2luZyBEYXRhIGJ5IENvdW50eSIpCmBgYAoKJChkKSQgSXMgdGhlIGRhaWx5IGF2ZXJhZ2Ugc25vd2ZhbGwgY29ycmVsYXRlZCB3aXRoIHRoZSBkYWlseSBtaXNzaW5nIHZhbHVlcyBwZXJjZW50PyAgT24gdGhlIGJhc2lzIG9mIHRoZXNlIHJlc3VsdHMsIHdoYXQgaXMgeW91ciBhc3Nlc3NtZW50IG9mIHRoZSByZWxpYWJpbGl0eSBvZiB0aGUgZGF0YSB0byBjYXB0dXJlIHRydWUgc25vd2ZhbGwgcGF0dGVybnM/IEluIG90aGVyIHdvcmRzLCBiYXNlZCBvbiB3aGF0IHlvdSd2ZSBkaXNjb3ZlcmVkLCBkbyB5b3UgdGhpbmsgdGhhdCB0aGUgbWlzc2luZyBkYXRhIGlzIGhpZ2hseSBwcm9ibGVtYXRpYywgb3Igbm90PwoKMS4gVGhlcmUgZG9lcyBub3Qgc2VlbSB0byBiZSBhIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGF2ZXJhZ2Ugc25vd2ZhbGwgYW5kIG1pc3NpbmcgdmFsdWVzLiAKMi4gR3JhcGhpbmcgYm90aCAqbmFfdG90YWxzKiBhbmQgZGFpbHkgKmF2ZXJhZ2UqIGJ5IGVhY2ggZGF5IHdlIHNlZSBhIHZlcnkgd2VhayBuZWdhdGl2ZSBjb3JyZWxhdGlvbi4KMy4gTmVnYXRpdmUgY29ycmVsYXRpb24gbWFrZXMgc2Vuc2UsIGdpdmVuIHRoYXQgaWYgKmF2ZXJhZ2UqIHNub3cgZmFsbCBpcyBoaWdoIHRoZW4gdGhlICp0b3RhbF9uYSogc2hvdWxkIGJlIGxvdy4KNC4gVGhlIGRhdGEgaXMgbm90IHRoYXQgcmVsaWFibGUgaW4gY2FwdHVyaW5nIHRydWUgc25vd2ZhbGwgcGF0dGVybnMuCjUuIFRoZSBtaXNzaW5nIGRhdGEgaXMgaGlnaGx5IHByb2JsZW1hdGljIGluIGNhcHR1cmluZyB0cnVlIHNub3dmYWxsIHBhdHRlcm5zLgoKYGBge3J9CnNub3cudXJsIDwtICJodHRwczovL3d3dy5uY2RjLm5vYWEuZ292L3Nub3ctYW5kLWljZS9kYWlseS1zbm93L05ZLXNub3dmYWxsLTIwMTgwMi5jc3YiCm1haW4uc25vdyA8LSBkYXRhLmZyYW1lKHJlYWQuY3N2KHNub3cudXJsLCBza2lwPTEpKQpzbm93IDwtIGFzLm1hdHJpeChtYWluLnNub3cpCnNub3dbc25vdyA9PSAiVCJdIDwtIDAuMDEKc25vd1tzbm93ID09ICJNIl0gPC0gTkEKc25vdyA8LSBhcy5kYXRhLmZyYW1lKHNub3cpCmBgYAojIyMjIyBDcmVhdGUgYSBGdW5jdGlvbiB0byBnZXQgKm5hX3RvdGFsKiBhbmQgKmF2ZXJhZ2UqIGZvciBlYWNoIGRheSBvZiB0aGUgbW9udGguCmBgYHtyfQpnZXQubmFfYXZnIDwtIGZ1bmN0aW9uKCl7CiAgZGF0ZXMgPC0gYygKICAgICJGZWIuMSIsIkZlYi4yIiwiRmViLjMiLCJGZWIuNCIsIkZlYi41IiwiRmViLjYiLCJGZWIuNyIsIkZlYi44IiwiRmViLjkiLCJGZWIuMTAiLAogICAgIkZlYi4xMSIsIkZlYi4xMiIsIkZlYi4xMyIsIkZlYi4xNCIsIkZlYi4xNSIsIkZlYi4xNiIsIkZlYi4xNyIsIkZlYi4xOCIsIkZlYi4xOSIsCiAgICAiRmViLjIwIiwiRmViLjIxIiwiRmViLjIyIiwiRmViLjIzIiwiRmViLjI0IiwiRmViLjI1IiwiRmViLjI2IiwiRmViLjI3IiwiRmViLjI4IgogICAgKQogIG5hX3RvdGFsIDwtIGxpc3QoKQogIGF2ZXJhZ2UgPC0gbGlzdCgpCiAgZGF0ZV9saXN0IDwtIGxpc3QoKQogIGZvcihkYXRlIGluIGRhdGVzKXsKICAgIG5hX3RvdGFsIDwtIGFwcGVuZChuYV90b3RhbCwgYXMubnVtZXJpYyhzdW0oaXMubmEoc25vd1tbZGF0ZV1dKSkpKQogICAgYXZlcmFnZSA8LSBhcHBlbmQoYXZlcmFnZSwgbWVhbihhcy5udW1lcmljKHNub3dbW2RhdGVdXSksIG5hLnJtID0gVFJVRSkpCiAgICBkYXRlX2xpc3QgPC0gYXBwZW5kKGRhdGVfbGlzdCwgZGF0ZSkKICB9CiAgdGVtcF9kZiA8LSBkby5jYWxsKHJiaW5kLmRhdGEuZnJhbWUsIE1hcCgnYycsIGRhdGVfbGlzdCwgbmFfdG90YWwsIGF2ZXJhZ2UpKQogIGNvbG5hbWVzKHRlbXBfZGYpIDwtIGMoJ2RhdGUnLCAnbmFfdG90YWwnLCAnYXZlcmFnZScpCiAgIyBzZXQgdXAgdGhlIGRhdGEgZnJhbWUKICB0ZW1wX2RmJGRhdGUgPC0gZmFjdG9yKHRlbXBfZGYkZGF0ZSwgbGV2ZWxzPWRhdGVzKQogIHRlbXBfZGYkbmFfdG90YWwgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIodGVtcF9kZiRuYV90b3RhbCkpCiAgdGVtcF9kZiRhdmVyYWdlIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHRlbXBfZGYkYXZlcmFnZSkpCiAgcmV0dXJuKHRlbXBfZGYpCn0KbmFfYXZnIDwtIGdldC5uYV9hdmcoKQpgYGAKIyMjIyMgR2V0IENvcnJlbGF0aW9uCmBgYHtyfQpjb3IobmFfYXZnJG5hX3RvdGFsLCBuYV9hdmckYXZlcmFnZSkKYGBgCiMjIyMjIFBsb3QgU2NhdHRlcgpgYGB7cn0KZ2dwbG90KG5hX2F2ZywgYWVzKG5hX3RvdGFsLCBhdmVyYWdlKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoKQpgYGAKIyMjIyMgUGxvdCBSZXN1bHRzIG92ZXIgVGltZSBmb3IgZnVuCmBgYHtyLCBmaWcud2lkdGg9MTN9CmdncGxvdChuYV9hdmcpICsgCiAgZ2VvbV9saW5lKGFlcyhkYXRlLCBuYV90b3RhbCwgY29sb3I9Im5hX3RvdGFsIiwgZ3JvdXAgPSAxKSkgKyAKICBnZW9tX2xpbmUoYWVzKGRhdGUsIGF2ZXJhZ2UsIGNvbG9yPSJhdmVyYWdlIiwgZ3JvdXAgPSAxKSkgKwogIGdndGl0bGUoIk5BIFRvdGFsIGFuZCBBdmVyYWdlIFNub3cgRmFsbCBGb3IgRmVicnVhcnkiKQpgYGAKIyMjIyMgTm9ybWFsaXplIERhdGEgVGhlbiBQbG90IEFnYWluCmBgYHtyfQojIEZ1bmN0aW9uIHRvIHRyYW5zZm9ybSBlYWNoIG5hX3RvdGFsIGFuZCBhdmVyYWdlIHRvIGJlZ2luIGF0IDEwLgpub3JtYWxpemUyIDwtIGZ1bmN0aW9uKGNvbCl7CiAgaW5kZXgudmFsdWUgPC0gbmFfYXZnW1tjb2xdXVsxXS8xMAogIGFucyA8LSBsaXN0KCkKICBmb3IoaSBpbiBuYV9hdmdbW2NvbF1dKXsKICAgIGFucyA8LSBhcHBlbmQoYW5zLCAoaS9pbmRleC52YWx1ZSkpCiAgfQogIHRlbXBfZGYgPC0gZGF0YS5mcmFtZShjb2w9dW5saXN0KGFucykpCiAgY29sbmFtZXModGVtcF9kZikgPC0gYyhjb2wpCiAgcmV0dXJuKHRlbXBfZGYpCn0KbmFfYXZnJG5hX3RvdGFsIDwtIG5vcm1hbGl6ZTIoJ25hX3RvdGFsJykkbmFfdG90YWwKbmFfYXZnJGF2ZXJhZ2UgPC0gbm9ybWFsaXplMignYXZlcmFnZScpJGF2ZXJhZ2UKYGBgCiMjIyMjIE5vcm1hbGl6ZWQgUGxvdApgYGB7ciwgZmlnLndpZHRoPTEzfQpnZ3Bsb3QobmFfYXZnKSArIAogIGdlb21fbGluZShhZXMoZGF0ZSwgbmFfdG90YWwsIGNvbG9yPSJuYV90b3RhbCIsIGdyb3VwID0gMSkpICsgCiAgZ2VvbV9saW5lKGFlcyhkYXRlLCBhdmVyYWdlLCBjb2xvcj0iYXZlcmFnZSIsIGdyb3VwID0gMSkpICsKICBnZ3RpdGxlKCJOQSBUb3RhbCBhbmQgQXZlcmFnZSBTbm93IEZhbGwgRm9yIEZlYnJ1YXJ5IChOb3JtYWxpemVkKSIpCmBgYAoKCg==